package com.spn.blog.login.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.spn.blog.common.dao.pojo.LoginUser;
import com.spn.blog.common.dao.pojo.SysUser;
import com.spn.blog.common.utils.JWTUtils;
import com.spn.blog.common.utils.RedisCacheUtil;
import com.spn.blog.common.utils.SPNUtil;
import com.spn.blog.common.vo.ErrorCode;
import com.spn.blog.common.vo.Result;
import com.spn.blog.common.vo.params.LoginParam;
import com.spn.blog.login.mapper.MenuMapper;
import com.spn.blog.service.email.MailService;
import com.spn.blog.service.login.LoginService;
import com.spn.blog.service.login.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;


import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@DubboService
@Component
@Transactional
public class LoginServiceImpl implements LoginService {

    @Autowired
    private SysUserService sysUserService;

    @DubboReference
    private MailService mailService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private RedisCacheUtil redisCacheUtil;

    //加密盐，在进行MD5加密的时候，加上这个
    private static final String salt = "spn!@#";

    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public Result login(LoginParam loginParam) {
        String account = loginParam.getAccount();
        String password = loginParam.getPassword();
        if (StringUtils.isBlank(account) || StringUtils.isBlank(password)) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMsg());
        }
        // 用户是否存在
        SysUser sysUser = sysUserService.findUserByAccount(account);
        if (null == sysUser) {
            return Result.fail(ErrorCode.ACCOUNT_PWD_NOT_EXIST.getCode(), ErrorCode.ACCOUNT_PWD_NOT_EXIST.getMsg());
        }
        // 密码是否正确
        boolean matches = password.length() >= 60 ?
                password.equals(sysUser.getPassword()) : passwordEncoder.matches(password + salt, sysUser.getPassword());
        if (!matches) {
            return Result.fail(ErrorCode.PWD_ERROR.getCode(), ErrorCode.PWD_ERROR.getMsg());
        }

        sysUser.setLoginIp(loginParam.getLoginip());

        // 获取权限，并存入redis
        List<String> authorities = sysUserService.selectPermsByUserId(sysUser.getId());
        LoginUser loginUser = new LoginUser(sysUser, authorities);
        redisCacheUtil.setCacheObject("TOKEN:" + sysUser.getId(), loginUser);

        //更新数据库
        sysUser.setLastLoginIp(sysUser.getLoginIp());
        sysUserService.updateById(sysUser);

        // 生成token返回前端
        String token = JWTUtils.createToken(sysUser.getId());
        log.info("TOKEN:" + token);
        return Result.success(token);
    }

    @Override
    public SysUser checkToken(String token) {
        if (StringUtils.isBlank(token)) {
            //无token
            return null;
        }
        Map<String, Object> stringObjectMap = JWTUtils.checkToken(token);
        if (stringObjectMap == null) {
            //有token，但是token不匹配
            return null;
        }
        //从redis里面取sysUser，效率比数据库取要高
        String userJson = redisTemplate.opsForValue().get("TOKEN_" + token);
        if (StringUtils.isBlank(userJson)) {
            //token匹配，但是redis不存在此token
            //原则上可以让用户获取信息，但是因为用户数量一旦增加，就会把数据库压垮，所以必须让用户从redis取token。
            return null;
        }
        //token校验通过，返回该sysUser信息
        SysUser sysUser = JSON.parseObject(userJson, SysUser.class);
        return sysUser;
    }

    @Override
    public Result logout(String token) {
        Map<String, Object> map = JWTUtils.checkToken(token);
        String userId = (String)map.get("userId");
        redisCacheUtil.deleteObject("TOKEN:" + userId);
        return Result.success(null);
    }


    @Autowired
    private MenuMapper menuMapper;



    @Override
    @Transactional
    public Result register(LoginParam loginParam) {
        // 参数校验
        String phonenumber = loginParam.getPhonenumber();
        String password = loginParam.getPassword();
        String nickname = loginParam.getNickname();
        String account = loginParam.getAccount();
        String email = loginParam.getEmail();
        if (StringUtils.isBlank(account) || StringUtils.isBlank(password) || StringUtils.isBlank(nickname)) {
            return Result.fail(ErrorCode.PARAMS_ERROR.getCode(), ErrorCode.PARAMS_ERROR.getMsg());
        }

        // 验证邮箱验证码是否合法
        String emailSCode = loginParam.getEmailSCode();
        String s = redisTemplate.opsForValue().get("EmailSCODE_" + loginParam.getEmail());
        if (!emailSCode.equals(s)) return Result.fail(
                ErrorCode.EMAIL_SECURITY_CODE_ERROR.getCode(), ErrorCode.EMAIL_SECURITY_CODE_ERROR.getMsg());

        //判断账号是否存在
        SysUser sysUser = sysUserService.findUserByAccount(account);
        if (sysUser != null) {
            return Result.fail(ErrorCode.ACCOUNT_EXIST.getCode(), ErrorCode.ACCOUNT_EXIST.getMsg());
        }

        // 删除邮箱验证码
        Boolean delete = redisTemplate.delete("EmailSCODE_" + loginParam.getEmail());
        if (!delete){
            System.out.println("请勿重复注册");
            return Result.fail(ErrorCode.ACCOUNT_EXIST.getCode(), ErrorCode.ACCOUNT_EXIST.getMsg());
        }

        //进行账号注册
        sysUser = new SysUser();
        sysUser.setNickname(nickname);
        sysUser.setAccount(account);
        sysUser.setPassword(passwordEncoder.encode(password + salt));
        sysUser.setLastLogin(new Date());
        sysUser.setAvatar("/static/img/logo.b3a48c0.png"); // 初始头像
        sysUser.setUserType("1");
        sysUser.setDeleted(0);
        sysUser.setStatus("0");
        sysUser.setEmail(email);
        sysUser.setMobilePhoneNumber(phonenumber);
        sysUser.setUserType("1");// 设置用户类型
        sysUser.setCreateDate(new Date());
        sysUser.setCreateBy("系统注册");
        sysUser.setUpdateDate(new Date());
        sysUser.setUpdateBy("系统注册");
        //save方法就会把sysUser存储进去，并且id自增
        sysUserService.save(sysUser);

        // 给普通用户赋予初始角色
        sysUserService.initAccessDenied(sysUser.getId());
        String token = JWTUtils.createToken(sysUser.getId());
        // 查询用户权限
        List<String> authorities = menuMapper.selectPermsByUserId(sysUser.getId());
        LoginUser loginUser = new LoginUser(sysUser,authorities);
        redisCacheUtil.setCacheObject("TOKEN:" + sysUser.getId(), loginUser, 1, TimeUnit.DAYS);
        return Result.success(token);


    }

    @Transactional
    @Override
    public Result sendVCode(String email) {
        // 检测邮箱是否为空
        if(StringUtils.isBlank(email)) return Result.fail(ErrorCode.EMAIL_ADDR_NULL_ERROR.getCode(),ErrorCode.EMAIL_ADDR_NULL_ERROR.getMsg());

        // 检测邮箱是否注册过
        SysUser sysUser = sysUserService.findUserByEmail(email);
        if (sysUser != null) return Result.fail(ErrorCode.EMAIL_EXIST.getCode(),ErrorCode.EMAIL_EXIST.getMsg());

        // 检测2分钟内是否有多次验证码请求发起
        String value = redisTemplate.opsForValue().get("EmailSCODE_" + email);
        if(StringUtils.isNotBlank(value)){
            return Result.fail(ErrorCode.EMAIL_SECURITY_CODE_RECOMMIT_ERROR.getCode(),ErrorCode.EMAIL_SECURITY_CODE_RECOMMIT_ERROR.getMsg());
        }

        // 自动生成验证码
        String random6 = SPNUtil.randomN(6);
        //redis进行缓存2分钟
        redisTemplate.opsForValue().set("EmailSCODE_" + email, random6, 2, TimeUnit.MINUTES);
        // 发送邮箱信息
        String title = "SPN博客验证码发送";
        String content = "您注册的博客验证码为："+ random6 + "，有效时间为2分钟。";
        mailService.sendSimpleMail(email,"",title,content);
        return Result.success("验证码已成功发送至邮箱" + email + "，2分钟之后失效");
    }



}
